package sweet.helper.container

import java.io.{ File, InputStream, FileInputStream }
import java.{ util=> jutil }
import scala.collection.{ mutable, immutable, jcl }
import StringHelper._

/** A container using #addProps and #addSysProps as abstract controls 
 * to manipulate #props member. 
 * 
 * This container doesn't have a corresponding managed object, but rather
 * providing a #props as lookup table for sub-classes to uses. The addXXX
 * methods will populate #props accordingly.
 */
trait PropsContainer {
  def props = _props.readOnly
  val _props = new mutable.HashMap[String, String]
  
  /** Add properties from Scala -> pairs. */
  def addProps(pairs: (String, String)*){
    _props ++= pairs
  }
  
  
  /** Add properties from another map object. */
  def addProps(hash: scala.collection.Map[String,String]){
    _props ++= hash
  }
  
  
  /** Find and add a property from System.getProperties or System.getEnv. 
   * First check property from System.getProperties. If not found then
   * translate name from "." to "_" and tries to find it from System.getEnv. If still 
   * not found then return the default value. 
   * Note if ENV value is found, both the orig name and translated name will be inserted with 
   * it's value! */
  def addSysProp(name: String, default: String){
    JSystem.props.get(name) match {
      case Some(sv) => _props += Pair(name, sv) //first case
      case None => {
        val envName = name.toUpperCase.fastReplace(".", "_")
        JSystem.env.get(envName) match {
          case Some(ev) => { //second case
            _props += Pair(name, ev)
            _props += Pair(envName, ev)
          }
          case None => _props += Pair(name, default) //third case
        }
      }
    }    
  }
  
  /** Import/load properties from File object. */
  def loadProps(file: File){
    loadProps(new FileInputStream(file))
  }
  
  /** Import/load properties from inputStream object. */
  def loadProps(ins: InputStream){
    val hash = new SProperties(JProperties(ins))
    _props ++= hash
  }
  
  /** Import/load properties from a resource string.
   * A resource string may have prefix that affect how to load it's content.
   * If prefix is classpath:, it will load from ClassLoader of this class as System Reources.
   * Else if it tries to use java.net.URI.toURL.toFile to load.
   * Else if above failed, it will load as a straigt file system name.
   */
  def loadProps(resource: String){    
    if(resource.startsWith("classpath:"))
      loadProps(resourceAsStream(resource))
    else
      try{ loadProps(new java.net.URI(resource).toURL.getFile) }
      catch{ case _ => loadProps(new File(resource)) }
  }
  
  /** Import/load proeprties from a resource that determine by #addSysProp. */
  def loadSysProps(name: String, default: String){
    addSysProp(name, default) 
    val resource = props(name)
    loadProps(resource)
  }
  
  /** this will let subclass to load a properties file as input stream by specifying
   * "classpath:/mypackage/myresource.txt" format. */
  def resourceAsStream(res: String): InputStream = {
    val clspath = "classpath:"
    val resName = if(res.startsWith(clspath)) res.substring(clspath.length) else res
    val stream = this.getClass.getClassLoader.getResourceAsStream(resName)
    if(stream == null) //give a more friendier exception msg instead of NPE.
      throw new Exception("Resource "+res+" can not be found")
    stream
  }
}

